package wili.demo.misc;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import wili.c.Ca;

/**
 * 属性树：支持与或子、键值对, 通过线索化支持对特征（属性）序列的穷举
 * 
 * @author weila 2021年7月14日
 */
@SuppressWarnings("all")
public abstract class AndOrKvNode<T extends AndOrKvNode> {
	public final int id = ++GenID;

	public String name = "";

	public T dad;

	/**
	 * 若值非空，必是叶结点（键值对）
	 */
	public Object value;

	/**
	 * 键值集（若子是键值对，不仅加入子集，还加入键值集）<br>
	 * 值是叶，对应到xml中的text. eg. <xy>text</xy>
	 */
	public Map<String, Object> kvMap;

	/**
	 * 子集
	 */
	public List<T> sons;

	/**
	 * true: 有与或子 | 与或键集
	 */
	protected final Boolean isOrNode;

	List<AndOrKvNode> posts;

	private int postInd;
	{
		if (id == 279)
			Ca.pause();
	}

	public String toString() {
		boolean ShowID = "".isEmpty();
		String idSt = "".isEmpty() ? id + "" : "";
		this.print(-1);
		if (sons == null) {
			if (this.value != null)
				return name + "=" + value;
			return idSt + name;
		} else {
			StringBuilder sb = new StringBuilder();
			sb.append(idSt + name + (this.isOrNode == null || this.isOrNode ? "{" : "["));
			for (int i = 0; i < sons.size(); i++) {
				if (i > 0)
					sb.append(", ");
				AndOrKvNode son = sons.get(i);
				sb.append(idSt + son.name);
				if (son.value != null) {
					sb.append("=" + son.value);
				}
			}
			sb.append((this.isOrNode == null || this.isOrNode ? "}" : "]"));
			return sb.toString();
		}
	}

	public AndOrKvNode(AndOrKvNode nod) {
		this.name = nod.name;
		this.isOrNode = nod.isOrNode;
		this.sons = new ArrayList<>(nod.sons);
		this.value = nod.value;
	}

	public AndOrKvNode(boolean isOr, String name) {
		this.isOrNode = isOr;
		this.name = name;
	}

	public AndOrKvNode(String name, Object value) {
		this.name = name;
		this.value = value;
		this.isOrNode = null;
	}

	@SuppressWarnings("unchecked")
	public AndOrKvNode setName(String name) {
		this.name = name;
		return this;
	}

	public void reset() {
		previousPath = null;
		postInd = 0;
		if (this.posts != null)
			this.posts.clear();
		if (sons != null) {
			for (AndOrKvNode son : sons) {
				son.reset();
			}
		}
	}

	private void addPost(AndOrKvNode pos) {
		if (posts == null)
			posts = new ArrayList<>(5);
		if (this.posts.contains(pos)) {
			Ca.asert(false, pos);
			return;
		}
		this.posts.add(pos);
	}

	private boolean _threadPost = false;

	/**
	 * 线索化
	 */
	public void threadPost() { //结点的后继是：左子|右兄|祖先右兄|无
		if (_threadPost)
			return;
		_threadPost = true;
		if (sons == null)
			return;
		this.posts = new ArrayList<>(sons.size());
		if (this.isOrNode) {
			//或子是其父的后继
			for (int i = 0; i < sons.size(); i++) {
				AndOrKvNode son = sons.get(i);
				//this.posts.add(son);
				this.addPost(son);
				son.threadPost();
			}
		} else {
			//与子的左子是父的后继
			this.posts.add(sons.get(0));
			for (int i = 0; i < sons.size(); i++) {
				AndOrKvNode son = sons.get(i);
				if (i > 0) {
					//结点的前驱是左兄右下叶|左兄
					AndOrKvNode left = sons.get(i - 1);
					List<AndOrKvNode> pres = left.getRdList();
					Ca.asert((left.sons == null) == (pres == null), "");
					if (pres == null) {
						left.addPost(son);
					} else {
						for (AndOrKvNode pre : pres) {
							pre.addPost(son);
						}
					}
				}
				son.threadPost();
			}
		}
	}

	public static String stringOfPath(Stack<AndOrKvNode> path) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < path.size(); i++) {
			AndOrKvNode ele = path.get(i);
			if (i > 0)
				sb.append(',');
			sb.append(ele.name);
		}
		return sb.toString();
	}

	private List<AndOrKvNode> rdList;

	/**
	 * 取得右下叶集
	 */
	private List<AndOrKvNode> getRdList() {
		if (sons == null)
			return null;
		if (rdList != null)
			return rdList;
		if (this.isOrNode) {
			rdList = new ArrayList<>(5 + sons.size());
			for (AndOrKvNode son : sons) {
				if (son.sons == null) {
					rdList.add(son);
				} else {
					List<AndOrKvNode> lis = son.getRdList();
					rdList.addAll(lis);
				}
			}
		} else {
			rdList = new ArrayList<>(5);
			AndOrKvNode son = sons.get(sons.size() - 1);
			if (son.sons == null) {
				rdList.add(son);
			} else {
				List<AndOrKvNode> lis = son.getRdList();
				rdList.addAll(lis);
			}
		}
		return rdList;
	}

	private Stack<AndOrKvNode> previousPath;

	/**
	 * 查找下一条线索路径
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T extends AndOrKvNode> Stack<T> findNextPath() {
		this.threadPost();
		boolean foundPath = previousPath != null;
		previousPath = new Stack<>();
		if (findNextPath00(foundPath, previousPath)) {
			return (Stack<T>) previousPath;
		}
		return null;
	}

	public void printAllPath() {
		System.out.println("=============printAllPath:");
		Stack<AndOrKvNode> path = this.findNextPath();
		int times = 0;
		while (path != null) {
			++times;
			System.out.println(times + ": " + path);
			path = this.findNextPath();
		}
		System.out.println("=============printAllPath:end.");
	}

	private boolean findNextPath00(boolean gotoPreviousPathEnd, Stack<AndOrKvNode> path) {
		path.add(this);
		if (id == 2)
			Ca.pause();
		if (gotoPreviousPathEnd) {//goto current thread end.
			if (posts == null)
				return false;
			for (int i = this.postInd; i < posts.size(); i++) {
				AndOrKvNode post = posts.get(i);
				postInd = i;
				if (post.findNextPath00(gotoPreviousPathEnd, path)) {
					return true;
				}
				gotoPreviousPathEnd = false;//findNext.
				path.pop();
			}
			return false;
		} else {
			if (posts == null)
				return true;
			for (int i = this.postInd; i < posts.size(); i++) {
				AndOrKvNode post = posts.get(i);
				postInd = i;
				post.postInd = 0;
				if (post.findNextPath00(gotoPreviousPathEnd, path)) {
					return true;
				}
				path.pop();
			}
			return false;
		}
	}

	@SuppressWarnings("unchecked")
	<T extends AndOrKvNode> T newInst___(boolean isOr, Object... sons) {
		try {
			Constructor<?> cons = this.getClass().getConstructor(boolean.class, Object[].class);
			return (T) cons.newInstance(isOr, sons);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@SuppressWarnings("unchecked")
	public <T extends AndOrKvNode> T newInst(int isOr, String name) {
		try {
			Constructor<?> cons = this.getClass().getConstructor(int.class, String.class);
			return (T) cons.newInstance(isOr, name);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@SuppressWarnings("unchecked")
	public <T extends AndOrKvNode> T newInst(String name, Object value) {
		try {
			Constructor<?> cons = this.getClass().getConstructor(String.class, Object.class);
			return (T) cons.newInstance(name, value);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@SuppressWarnings("unchecked")
	public <T extends AndOrKvNode> T sets(String... keys) {
		for (String key : keys) {
			set(key, 1);
		}
		return (T) this;
	}

	private void putKeyVaue00(AndOrKvNode son) {
		if (son.value == null)
			return;
		if (kvMap == null)
			kvMap = new HashMap<>();
		if (this.kvMap.containsKey(son.name)) {
			Ca.asert(false, son.name);
			return;
		}
		this.kvMap.put(son.name, son.value);
	}

	/**
	 *
	 */
	public T set(String key, Object value) {
		AndOrKvNode son = newInst(key, value);
		putKeyVaue00(son);
		addSon(son);
		return (T) this;
	}

	@SuppressWarnings("unchecked")
	public T son(int ind) {
		return (T) (sons.get(ind < 0 ? sons.size() + ind : ind));
	}

	public <T extends AndOrKvNode> T son(String name) {
		if (sons == null)
			return null;
		for (AndOrKvNode son : sons) {
			if (son.name.equals(name))
				return (T) son;
		}
		return null;
	}

	public void addSons(Object... objs) {
		for (Object ele : objs) {
			addSon(ele);
		}
	}

	/**
	 *
	 */
	public T addSon(Object obj) {
		int isOr = 1;
		if (obj == null)
			Ca.pause();
		Ca.asert(obj != this, obj);
		if (this.sons != null && sons.contains(obj)) {
			return (T) sons.get(sons.indexOf(obj));
		}
		T son;
		if (obj instanceof String) {
			son = this.newInst(isOr, obj.toString());
		} else {
			Ca.asert(obj instanceof AndOrKvNode, obj.getClass());
			son = (T) obj;
		}
		if (sons == null) {
			sons = new ArrayList<>();
		}
		sons.add(son);
		return (T) son;
	}

	public void sortSons() {
		if (!name.equals("VarG"))
			return;
		Collections.sort(this.sons, new Comparator<AndOrKvNode>() {
			@Override
			public int compare(AndOrKvNode o1, AndOrKvNode o2) {
				return o1.name.compareTo(o2.name);
			}
		});
	}

	public void print(int opt) {
		if (opt < 0)
			return;
		StringBuilder sb;
		System.out.println("id" + this.id + "---------print:");
		print00("\n");
		//System.out.println(sb.toString().trim());
	}

	private void print00(String dent) {
		boolean ShowID = "".isEmpty();
		if (this.sons == null) {
			System.out.println(dent + this.toString());
			return;
		} else {
			this.sortSons();
			String idSt = ShowID ? id + ":" : "";
			String st = dent + idSt + name + (this.isOrNode == null || this.isOrNode ? "{" : "[");
			System.out.print(st);
			String dent2 = dent + "   ";
			if (id == 71)
				Ca.pause();
			for (int i = 0; i < this.sons.size(); i++) {
				AndOrKvNode son = sons.get(i);
				if (i > 0) {
					System.out.print(",");
				}
				if (son.value != null) {
					st = dent2 + son.name + "=" + son.value;
					System.out.print(st);
				} else if (son.sons != null) {
					son.print00(dent2);
				} else {
					System.out.print(dent2 + son);
				}
			}
			st = dent + (this.isOrNode == null || this.isOrNode ? "}" : "]");
			if (this.name.equals("body")) {
				st += "//" + this.name;
			}
			System.out.print(st);
		}
	}

	public void printThread() {
		StringBuilder sb;
		printThread00("\n", sb = new StringBuilder());
		System.out.println("---------printThread:");
		System.out.println(sb.toString().trim());
	}

	private void printThread00(String dent, StringBuilder sb) {
		if (posts != null) {
			sb.append(dent + id + ":" + name);
			String dent2 = dent + "  ";
			if (id == 2)
				Ca.pause();
			for (int i = 0; i < posts.size(); i++) {
				AndOrKvNode post = posts.get(i);
				post.printThread00(dent2, sb);
				if (i + 1 < posts.size()) {
					sb.append(", ");
				}
			}
		} else {
			sb.append(dent + id + ":" + name);
		}
	}

	/**
	 * 比较两结点的相似度: rate[1]/rate[0 ];
	 * 
	 * @param nod
	 * @param rate {allNum, sameNum}
	 * @return
	 */
	public void compare(AndOrKvNode nod, int[] rate) {
		rate[0] += 2;
		if (this.name.equals(nod.name)) {
			rate[1]++;
			if (this.name.equals("varName")) {
				rate[1]++;//todo:变量名也当参与比较
			} else {
				if (this.value != null) {
					if (this.value.equals(nod.value)) {
						rate[1]++;
					}
				}
			}
		}
		if (sons == null) {
			return;
		}
		int sonNum = nod.sonNum();
		for (int i = 0; i < sons.size(); i++) {
			AndOrKvNode son = sons.get(i);
			if (i < sonNum) {
				son.compare(nod.son(i), rate);
			} else {
				rate[0] += (sons.size() - i);
				return;
			}
		}
	}

	public String name() {
		return this.name;
	}

	public void setValue(Object value) {
		this.value = value;
	}

	public int sonNum() {
		if (sons == null)
			return 0;
		return sons.size();
	}

	public <V> V value() {
		return (V) this.value;
	}

	public Map<String, Object> getMap() {
		return this.kvMap;
	}

	private int nodeNum;

	public int getNodeNum(boolean refresh) {
		if (this.sons == null)
			return 1;
		if (!refresh && nodeNum != 0)
			return nodeNum;
		int ret = 1;
		for (AndOrKvNode son : sons) {
			ret += son.getNodeNum(refresh);
		}
		return this.nodeNum = ret;
	}

	static int GenID;
}
